package com.black.knight.chess.plugins;

import java.util.ArrayList;
import java.util.Set;

import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.TextView;

import com.black.knight.chess.R;
import com.black.knight.chess.SahMatActivity;
import com.black.knight.chess.components.CustomizeLoadingDialog;
import com.black.knight.chess.domain.ChessCommand;
import com.black.knight.chess.domain.DeviceItem;
import com.black.knight.chess.domain.Player;
import com.black.knight.chess.enums.CommandType;
import com.black.knight.chess.enums.NewGame;
import com.black.knight.chess.model.SahModel;
import com.black.knight.chess.model.SettingsModel;
import com.black.knight.chess.plugins.support.BluetoothChessService;
import com.black.knight.chess.utils.Utils;

public class BluetoothPlugin extends ChessPlugin {
	
	private static BluetoothPlugin plugin;
	
	public static int REQUEST_ENABLE_BT = 9001;
	
	private BluetoothAdapter mBluetoothAdapter;
	
	private ArrayList<DeviceItem> devices = new ArrayList<DeviceItem>();
	
	private AlertDialog alert;
	
	private BroadcastReceiver mReceiver;
	
	private String mConnectedDeviceName = null;
	
	private BluetoothChessService service;
	
	private CustomizeLoadingDialog pDialog;
	
	private CustomizeLoadingDialog waitingForResponseDialog;
	
	//private AlertDialog pauseGameDialog;
	
	private Handler handler; 
	
	private boolean host;
	
	private String fen;
	
	public BluetoothPlugin() {
		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		
		pDialog = new CustomizeLoadingDialog(SahModel.context, "");
		
		waitingForResponseDialog = new CustomizeLoadingDialog(SahModel.context, "");
		
		this.handler = new Handler() {
	        @Override
	        public void handleMessage(Message msg) {
	        	switch (msg.what) {
	            	case BluetoothChessService.MESSAGE_STATE_CHANGE:
		                switch (msg.arg1) {
			                case BluetoothChessService.STATE_CONNECTED:
			                    break;
			                case BluetoothChessService.STATE_CONNECTING:
			                    break;
			                case BluetoothChessService.STATE_LISTEN:
			                case BluetoothChessService.STATE_NONE:
			                    break;
		                }
		                break;
		            case BluetoothChessService.MESSAGE_WRITE:
		                byte[] writeBuf = (byte[]) msg.obj;
		                
		                Object obj = Utils.bytesToObject(writeBuf);;
		                if(obj != null) {
		                	ChessCommand cmd = (ChessCommand)obj;
		                	
		                	handleCommand(cmd, false);
		                }
		                break;
		            case BluetoothChessService.MESSAGE_READ:
		                byte[] readBuf = (byte[]) msg.obj;
		                Object obj1 = Utils.bytesToObject(readBuf);;
		                if(obj1 != null) {
		                	ChessCommand cmd = (ChessCommand)obj1;
		                	
		                	handleCommand(cmd, true);
		                }
		                break;
		            case BluetoothChessService.MESSAGE_DEVICE_NAME:
		                mConnectedDeviceName = msg.getData().getString(BluetoothChessService.DEVICE_NAME);
		                
		                if(host) {
		                	try {	
		                		waitingForResponseDialog.setMessage(SahModel.context.getResources().getString(R.string.waiting_for_response));
			                	waitingForResponseDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {						
			    					@Override
			    					public void onCancel(DialogInterface dialog) {
			    						stop();
			    					}
			    				});
		                		waitingForResponseDialog.show();
		                	} catch (Exception e) { }
		                	
		                	ChessCommand cmd = new ChessCommand();
		                	cmd.setType(CommandType.NEW_GAME_REQUEST);
		                	cmd.setGameDuration(SahModel.getInstance().getGame().getGameDuration());
		                	cmd.setPlayOnClock(SettingsModel.getInstance().isPlayOnClock());
		                	cmd.setFen(getFen());
		                	
			                Player player = new Player(mBluetoothAdapter.getName());
			                player.setWhitePlayer(true);
			                cmd.setPlayer(player);
			                sendCommand(cmd);
			                
			                SahModel.getInstance().getGame().setPlayOnClock(SettingsModel.getInstance().isPlayOnClock());
		                } else {
		                	try {
		                		pDialog.setMessage(SahModel.context.getResources().getString(R.string.connected_to) + " " + mConnectedDeviceName + "! " + SahModel.context.getResources().getString(R.string.waiting_for_player));
		                	} catch (Exception e) { }
		                }
		                
		                break;
		            case BluetoothChessService.MESSAGE_TOAST:
		                try {
		                	Utils.displayToastMessage(SahModel.context, msg.getData().getString(BluetoothChessService.TOAST));
		                } catch (Exception e) { }
		                break;
	            }
	        }
	    };
		this.service = new BluetoothChessService(SahModel.context, handler);
	}
	
	public static BluetoothPlugin getInstance() {
		if(plugin == null) {
			plugin = new BluetoothPlugin();
		}
		
		return plugin;
	}
	
	private void handleCommand(final ChessCommand cmd, boolean received) {
		if(CommandType.MOVE.equals(cmd.getType())) {			
			try {
				SahModel.context.clearFilters();
				
				if(SahModel.context.playMove(cmd.getMove())) {
					SahModel.context.setMove(null);
	        	}
			} catch (Exception e) { 
				try {
					Utils.displayAlertMessage(SahModel.context, SahModel.context.getResources().getString(R.string.unknown_error));
				} catch (Exception e1) { }
			}
			
			return;
		} 		
		
		if(received) {			
			if(CommandType.STOP.equals(cmd.getType())) {
		    	
				try {
					if(!SahModel.context.isFinishing()) {
						Utils.chooseNewGameType(SahModel.context);
					}
				} catch (Exception e) { }
				
				return;
			}
			
			if(CommandType.NEW_GAME_REQUEST.equals(cmd.getType())) {
				try {
					pDialog.hide();
				
					AlertDialog.Builder builder = new AlertDialog.Builder(SahModel.context);
			    	builder.setTitle(SahModel.context.getResources().getString(R.string.accept_player));
			    	builder.setMessage(SahModel.context.getResources().getString(R.string.accept_player) + ": " + cmd.getPlayer().getName());
			    	builder.setCancelable(true);
			    	builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
						@Override
						public void onCancel(DialogInterface dialog) {
							ChessCommand cmd1 = new ChessCommand();
			            	cmd1.setType(CommandType.NEW_GAME_DECLINATION);
			                sendCommand(cmd1);
			                try {
				                pDialog.setMessage(SahModel.context.getResources().getString(R.string.waiting_for_player));
			                	pDialog.show();
			                } catch (Exception e) { }
						}
					});
			    	builder.setPositiveButton(R.string.accept, new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							SahModel.getInstance().reset(cmd.getFen());
							SahModel.getInstance().getGame().getPlayer1().setName(mBluetoothAdapter.getName());
							SahModel.getInstance().getGame().getPlayer1().setWhitePlayer(false);
			            	
			            	SahModel.getInstance().getGame().setPlayer2(cmd.getPlayer());
			            	
		            		SahModel.getInstance().getGame().setCurrentPlayer(SahModel.getInstance().getGame().getPlayer2());
			            	
			            	SahModel.getInstance().getGame().setGameDuration(cmd.getGameDuration());
			            	SahModel.getInstance().getGame().setPlayOnClock(cmd.isPlayOnClock());
			            	
			            	ChessCommand cmd1 = new ChessCommand();
			            	cmd1.setType(CommandType.NEW_GAME_ACCEPTANCE);
			                cmd1.setPlayer(SahModel.getInstance().getGame().getPlayer1());
			                cmd1.setFen(cmd.getFen());
			                sendCommand(cmd1);
			                try {
			                	SahModel.context.playNewGame(NewGame.BLUETOOTH, false);
			                } catch (Exception e) { }
						}
					});
			    	builder.setNegativeButton(R.string.decline, new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							ChessCommand cmd1 = new ChessCommand();
			            	cmd1.setType(CommandType.NEW_GAME_DECLINATION);
			                sendCommand(cmd1);
			                try {
				                pDialog.setMessage(SahModel.context.getResources().getString(R.string.waiting_for_player));
			                	pDialog.show();
			                } catch (Exception e) { }
						}
					});
			    	
			    	AlertDialog acceptPlayer = builder.create();
					acceptPlayer.show();
				} catch (Exception e) { }
            } else if(CommandType.NEW_GAME_ACCEPTANCE.equals(cmd.getType())) {
            	try {
            		waitingForResponseDialog.hide();
	            	SahModel.getInstance().reset(cmd.getFen());
	            	SahModel.getInstance().getGame().getPlayer1().setName(mBluetoothAdapter.getName());
					SahModel.getInstance().getGame().getPlayer1().setWhitePlayer(true);
					
					SahModel.getInstance().getGame().setCurrentPlayer(SahModel.getInstance().getGame().getPlayer1());
	            	
	            	SahModel.getInstance().getGame().setPlayer2(cmd.getPlayer());
	            	
	            	Utils.displayToastMessage(SahModel.context, SahModel.context.getResources().getString(R.string.request_accepted));
	            	SahModel.context.playNewGame(NewGame.BLUETOOTH, false);
            	} catch (Exception e) { }
            } else if(CommandType.NEW_GAME_DECLINATION.equals(cmd.getType())) {
            	try {
            		waitingForResponseDialog.hide();
            		Utils.displayToastMessage(SahModel.context, SahModel.context.getResources().getString(R.string.request_declined));
            	} catch (Exception e) { }
            	stop();
            }
		}
	}
	
	public boolean sendCommand(ChessCommand cmd) {
		byte[] bytes = Utils.objectToBytes(cmd);
		
		if(bytes != null) {
			service.write(bytes);
			return true;
		}
		
		return false;
	}
	
	public void stop() {
		service.stop();
	}
	
	public void start() {
		if (!mBluetoothAdapter.isEnabled()) {
		    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
		    try {
		    	SahModel.context.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
		    } catch (Exception e) { }
		} else {
    		devices.clear();
    		if(host) {
    			chooseBlueToothPairedDevice(SahModel.context);
    		} else {
	    		try {
    				pDialog.setMessage(SahModel.context.getResources().getString(R.string.waiting_for_player));
	    	    	
		    		pDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {						
						@Override
						public void onCancel(DialogInterface dialog) {
							stop();
						}
					});
	    			pDialog.show();
	    		} catch (Exception e) { }
    			this.service.start();
    		}
		}
	}
	
	public void onResult(int resultCode, Intent data) {
		if(resultCode == Activity.RESULT_OK) {
			devices.clear();
    		if(host) {
    			chooseBlueToothPairedDevice(SahModel.context);
    		} else {
	    		try {
    				pDialog.setMessage(SahModel.context.getResources().getString(R.string.waiting_for_player));
		    		pDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {						
						@Override
						public void onCancel(DialogInterface dialog) {
							stop();
						}
					});
	    		
	    			pDialog.show();
	    		} catch (Exception e) { }
    			
	    		this.service.start();
    		}
		} else if(resultCode == Activity.RESULT_CANCELED) {
			try {
				Utils.displayToastMessage(SahModel.context, SahModel.context.getResources().getString(R.string.bluetooth_not_enabled));
			} catch (Exception e) { }
		}
	}
	
	public void chooseBlueToothPairedDevice(final SahMatActivity activity) {
		try {
			this.service.start();
	    	Set<BluetoothDevice> pairedDevices = BluetoothPlugin.getInstance().getmBluetoothAdapter().getBondedDevices();
	    	
	    	if (pairedDevices.size() > 0) {
	    	    for (BluetoothDevice device : pairedDevices) {
	    	    	DeviceItem dv = new DeviceItem(device.getName() + "\n" + device.getAddress(), R.drawable.bluetooth);
		            dv.setDevice(device);
		            
	    	    	devices.add(dv);
	    	    }
	    	}
	    	
	    	devices.add(new DeviceItem(SahModel.context.getResources().getString(R.string.discover_new_devices), R.drawable.empty));
	    	devices.add(new DeviceItem(SahModel.context.getResources().getString(R.string.make_discoverable_this_device), R.drawable.empty));
	    	
	    	ListAdapter adapter = new ArrayAdapter<DeviceItem>(activity, android.R.layout.select_dialog_item, android.R.id.text1, devices){
		        public View getView(int position, View convertView, ViewGroup parent) {
		            View v = super.getView(position, convertView, parent);
		            TextView tv = (TextView)v.findViewById(android.R.id.text1);
	            	tv.setTextSize(16);
	            	
		            if(devices.get(position).icon != R.drawable.empty) {
		            	tv.setCompoundDrawablesWithIntrinsicBounds(devices.get(position).icon, 0, 0, 0);
	
		            	int dp5 = (int) (5 * activity.getResources().getDisplayMetrics().density + 0.5f);
		            	tv.setCompoundDrawablePadding(dp5);
		            }
	
		            return v;
		        }
			};
			
	    	AlertDialog.Builder builder = new AlertDialog.Builder(activity);
	    	builder.setTitle(SahModel.context.getResources().getString(R.string.play_new_game_against));
	    	builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
	    	    public void onClick(DialogInterface dialog, int item) {
	    	    	DeviceItem item1 = devices.get(item);
	    	    	if(item1.text == SahModel.context.getResources().getString(R.string.discover_new_devices)) {
	    	    		devices.remove(item1);
	    	    		discoverDevice(activity);
	    			} else if(item1.text == SahModel.context.getResources().getString(R.string.make_discoverable_this_device)){
	    				Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
	    				discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
	    				activity.startActivity(discoverableIntent);
	    			} else {
	    				service.stop();
	    				
	    				service.connect(item1.getDevice());
	    			}
	    	    }
	    	});
	    	
	    	alert = builder.create();
	    	alert.show();
		} catch (Exception e) { }
	}
	
    public void discoverDevice(final SahMatActivity activity) {
    	try {
	    	mReceiver = new BroadcastReceiver() {
			    public void onReceive(Context context, Intent intent) {
			        String action = intent.getAction();
			        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
			            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			            
			            for (DeviceItem item : devices) {
							if((device.getName() + "\n" + device.getAddress()).equals(item.text)) return;
						}
			            
			            DeviceItem dv = new DeviceItem(device.getName() + "\n" + device.getAddress(), R.drawable.bluetooth);
			            dv.setDevice(device);
			            
			            devices.add(devices.size() - 1, dv);
				        if(alert != null) {
				        	alert.hide();
				        }
				        
				        showDiscoveredDevices(activity);
			        }
			    }
	    	};
	    	
	    	IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
	    	activity.registerReceiver(mReceiver, filter);
	    	BluetoothPlugin.getInstance().getmBluetoothAdapter().startDiscovery();
	    	
	    	showDiscoveredDevices(activity);
    	} catch (Exception e) { }
    }
    
    public void showDiscoveredDevices(final SahMatActivity activity) {
       	try {
	    	ListAdapter adapter = new ArrayAdapter<DeviceItem>(activity, android.R.layout.select_dialog_item, android.R.id.text1, devices){
		        public View getView(int position, View convertView, ViewGroup parent) {
		            View v = super.getView(position, convertView, parent);
		            TextView tv = (TextView)v.findViewById(android.R.id.text1);
		            
		            tv.setTextSize(16);
	
		            if(devices.get(position).icon != R.drawable.empty) {
		            	tv.setCompoundDrawablesWithIntrinsicBounds(devices.get(position).icon, 0, 0, 0);
	
		            	int dp5 = (int) (5 * activity.getResources().getDisplayMetrics().density + 0.5f);
		            	tv.setCompoundDrawablePadding(dp5);
		            }
	
		            return v;
		        }
			};
	    	
	    	AlertDialog.Builder builder = new AlertDialog.Builder(activity);
	    	builder.setTitle(SahModel.context.getResources().getString(R.string.play_new_game_against));
	    	builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
				@Override
				public void onCancel(DialogInterface dialog) {
					Utils.displayToastMessage(activity, SahModel.context.getResources().getString(R.string.device_discovery_shutdown) + "...");
					cancelDiscovery(activity, BluetoothPlugin.getInstance().getmBluetoothAdapter(), mReceiver);
				}
			});
	    	builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
	    	    public void onClick(DialogInterface dialog, int item) {
	    	    	DeviceItem item1 = devices.get(item);
	    	    	if(item1.text == SahModel.context.getResources().getString(R.string.make_discoverable_this_device)){
	    				Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
	    				discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
	    				activity.startActivity(discoverableIntent);
	    				
	    				showDiscoveredDevices(activity);
	    			} else {
	    				service.connect(item1.getDevice());
	    				
	    				cancelDiscovery(activity, BluetoothPlugin.getInstance().getmBluetoothAdapter(), mReceiver);
	    			}
	    	    }
	    	});
	    	
	    	alert = builder.create();
	    	alert.show();
       	} catch (Exception e) { }
    }
    
    public static void cancelDiscovery(SahMatActivity activity, BluetoothAdapter adapter, BroadcastReceiver receiver) {
    	try {
	    	if(null != receiver) {
	    		adapter.cancelDiscovery();
				activity.unregisterReceiver(receiver);
			}
    	} catch (Exception e) { }
    }
    
	public BluetoothChessService getService() {
		return service;
	}

	public BluetoothAdapter getmBluetoothAdapter() {
		return mBluetoothAdapter;
	}

	public boolean isHost() {
		return host;
	}

	public void setHost(boolean host) {
		this.host = host;
	}

	public String getFen() {
		return fen;
	}

	public void setFen(String fen) {
		this.fen = fen;
	}
}